#ifndef H_PARTICLE
#define H_PARTICLE

#include "vector.h"
#include "debug.h"
#include "animatedDrawable.h"
#include "list.h"
#include <math.h>
#include <stdlib.h>
#include <list>

// This defs are meant to be use to ease the definition
// of particles with similar properties eg:
// UINT massive = PARTIVLE_HAS_MASS;
// UINT charges = PARTICLE_HAS_MASS | PARTICLE_HAS_CHARGE

#define PARTICLE_NO_INTERACTIONS 0x0
#define PARTICLE_IS_SOLID 0x1
#define PARTICLE_HAS_MASS 0x2
#define PARTICLE_HAS_FPS 0x4
#define PARTICLE_IS_UNELASTIC 0x8

class Circle {
   public:
      float radius;
      float x; //for the center position
      float y;

      Circle ( float _radius, float _x, float _y ) {
	 ARG(radius);
	 ARG(x);
	 ARG(y);
      }
      
      void setXY( float _x, float _y ) {
	 ARG(x);
	 ARG(y);
      }
};

//A particle descriprtion is class used to describe all the particles with
//similar properties. For examples all asteroids are solid and have the same
//mass
class ParticleDescription {
   public:
      //each particle is contained in a circle that is used when checking for
      //collisions. Checking circle collisions seems easier the rectangle
      //collisions

      Circle* circle;

      UINT properties;

      UINT fps;

      //physical properties
      float mass;

      ParticleDescription ( UINT _properties, float _radius, float _x, float _y, float _mass ) {      //#D
	 properties = _properties;
	 fps = 0;
	 circle = new Circle (_radius,_x, _y );
	 ARG( mass );
      }

      void setFps(float _fps) {	    //#D
	 if (_fps != 0. ) fps = (UINT) floor(1000./_fps); else fps=0;
      }
};

class Constraint;

class Particle  {
   private:
      Drawable* drawable;
      UINT start_ticks;
      std::list<Constraint*>* volatileConstraints;
      std::list<Constraint*>* staticConstraints;

      //this is true if the  constraint is aplied to tha particle more than one actualization default is false

   public:
      ParticleDescription* pd;
      Circle* circle;

      Drawable* getDrawable() {	    //#D
	return drawable;
      }	

      Vector pos;
      Vector vel;
      Vector acc;

      //particles with sprite not animated
      Particle (  Vector const & _pos,		//#D
		  Vector const & _vel,		
		  Vector const & _acc,		
		  Sprite * _sprite, ParticleDescription* _pd ) {		
	 pos = _pos; 
	 vel = _vel; 
	 acc = _acc;
	 pd = _pd;
	 start_ticks = 0;
	 volatileConstraints = new std::list<Constraint*> ();
	 staticConstraints = new std::list<Constraint*> ();

	 if ( ! _sprite->animated ) {
	    drawable = new Drawable ( _sprite );
	 } 
	 else {
	    drawable = new AnimatedDrawable( _sprite );
	 }

	 actualizeSprite(0);

	 circle = new Circle(*pd->circle);
      }

     void actualizeSprite ( UINT ticks ) {	 //#D

	 drawable->getPosRect()->x =  (int) floor ( pos.getx() );
	 drawable->getPosRect()->y =  (int) floor ( pos.gety() );

	 if ( pd->properties & PARTICLE_HAS_FPS ) {
	    if ( (pd->fps != 0.) && ( ticks - start_ticks > pd->fps ) ){
	       drawable->advance();
	       start_ticks = ticks;
	    }
	 }
     }

     virtual void resetAcc() {	    //#D
	acc.set(0.,0.);
     }

     void addConstraint ( Constraint & );
     void honorConstraints ();
     const Circle *getCircle() const;
};

class ParticleListNode : public ListNode { //Double linked list
   public:
      ParticleListNode	(  ParticleListNode* _prev,	 //#D
			   Particle & _part,	
			   ParticleListNode* _next ) : ListNode ( _prev, (Listable&) _part,_next ) {			
      }
};

class ParticleList: public List{
   public:
      ParticleList () : List () { //#D
      }
      void add ( Particle * );
      Particle * iter ();
};

// This is the abstrac class of constraint the first subclass i'll code will be
// the plane constraint

class Constraint: public Listable {
   public:
      bool longLasting; 
      Constraint ();
      virtual void honor ( Particle* ) {}
      void toggleLasting () { longLasting =!longLasting; }
      bool lasting () { return longLasting; }
};

class PlaneConstraint : public Constraint {
   private:
      Vector normal;
      Vector position;
   public:
      PlaneConstraint ( Vector const &, Vector const &);
      virtual void honor (  Particle* );
};

class PhysicsContext {
   public:
      Vector *start;
      float offset;
      float width;
      float height;
      float timeResulution;
      Timer* timer;
      PhysicsContext (	const float _width,		 //#D
			const float _height,		 
			const float _timeResolution,
			const float _offset,
			Timer* _timer,
	   const Vector & _start ) {		     
	 width = _width; 
	 height = _height; 
	 timeResulution = _timeResolution;
	 timer = _timer;
	 ARG(offset);
	 start = new Vector ( _start );
      }

};

class CollisionData {
   public:
      float dist; //Distance between the particles involved
      float diameter; //sum of the radius of the particles

      CollisionData( float _dist, float _diameter ) {
	 set(_dist,_diameter);
      }

      void set( float _dist, float _diameter ) {
	 ARG(dist);
	 ARG(diameter);
      }

      void sqrtDists() {
	 dist = sqrt(dist);
	 diameter = sqrt(diameter);
      }
};

typedef void (*fieldAction)(Particle*, PhysicsContext*);
typedef bool (*collisionCheckfp)(Particle*, Particle*, CollisionData** );

class Field: public Listable {
   public:
      Field ( fieldAction _fap ) {     //#D
	 fap = _fap;
      }
      fieldAction fap;
};

class ParticleEngine {
   private:
      std::list<Field> fields;
      std::list<Particle*> particles;
      std::list<collisionCheckfp> collisionCheckfps; //collision check function pointers

      DrawableList* drawableList;

      float deltaTime;
      PhysicsContext *pc;
   public:
      ParticleEngine ( PhysicsContext * _pc, DrawableList* _drawableList ) {		       //#D
	 drawableList = _drawableList;
	 pc = _pc; 
      }
      void add ( Particle & );
      void addField ( Field & );
      void addCollisionCheck ( collisionCheckfp );

      void actualize ();
      void checkCollisions();
      void applyFields ();
      void removeParticles ();
      void checkCollision ( Particle*, Particle* );
};

//returns 1 if collision 0 otherwise
int checkRectCollision( Rect&, Rect& );
bool checkCircleCollision ( const Circle &a, const Circle &b, CollisionData** cd );
bool solidCollision ( Particle*, Particle*, CollisionData** );

#endif
